﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.UI;
using System.Reflection;
using Ue.Core.DynamicReflection;
using Ue.Extensions;

namespace Ue.Infrastructure.UI.SkinnedControls.PartAutoMappings
{
    public class PartAutoMapping : IPartAutoMapping
    {
        private readonly IMappingControlProvide provide;
        private static IDictionary<Type, PropertyInfo[]> properties;
        private IDictionary<Type, IList<MappingEntry>> mappings;

        static PartAutoMapping()
        {
            PartAutoMapping.properties = new Dictionary<Type, PropertyInfo[]>();
        }

        public PartAutoMapping(IMappingControlProvide provide)
        {
            this.provide = provide;
            this.mappings = new Dictionary<Type, IList<MappingEntry>>();
        }

        #region IPartAutoMapping

        public IMappingControlProvide Provide
        {
            get { return this.provide; }
        }

        public IEnumerable<MappingEntry> GetMappings<T>()
        {
            var type = typeof(T);

            return this.mappings.Keys.Contains(type)
                ? this.mappings[type]
                : Enumerable.Empty<MappingEntry>();
        }

        public IPartAutoMapping RegisterMapping<T>(string entityPropertyName, string controlName)
        {
            return this.RegisterMapping<T>(entityPropertyName,controlName, string.Empty);
        }
        public IPartAutoMapping RegisterMapping<T>(string entityPropertyName, string controlName, string formatString)
        {
            var type = typeof(T);
            var propertyChain = this.BuildPropertyChain(type, entityPropertyName);
            if (!this.mappings.Keys.Contains(type))
            {
                this.mappings[type] = new List<MappingEntry>();
            }

            this.mappings[type].Add(new MappingEntry(
                () => this.provide.FindControl(controlName),
                propertyChain,
                formatString));


            return this;
        }

        public T ToPart<T>(T entity)
        {
            return this.EntityHandler(entity, (control, propertyChain, formatString) =>
            {
                var value = control.GetValue();
                if (value != null)
                {
                    this.SetPropertyValue(entity, propertyChain, value);
                }
            });
        }

        public void ToView<T>(T entity)
        {
            this.EntityHandler(entity, (control, propertyChain, formatString) =>
            {
                var value = this.GetPropertyValue(entity, propertyChain);
                ////if (value != null)
                ////{
                try
                {
                    control.SetValue(string.IsNullOrEmpty(formatString)
                            ? value.ToString()
                            : value.Format(formatString));
                }
                catch { }
                //}

            });
        }
        #endregion

        #region Helper

        private T EntityHandler<T>(T entity, Action<Control, PropertyChain, string> mappingEntryHandler)
        {
            if (entity == null)
            {
                return default(T);
            }

            IEnumerable<MappingEntry> entries = this.GetMappingEntries(entity.GetType());
            foreach (var mappingEntry in entries)
            {
                var control = mappingEntry.Target;
                if (control != null)
                {
                    mappingEntryHandler(control,mappingEntry.PropertChain, mappingEntry.FormatString);
                }
            }

            return entity;
        }

        private IEnumerable<MappingEntry> GetMappingEntries(Type type)
        {
            var entries = this.mappings.Keys.Contains(type)
                ? this.mappings[type]
                : Enumerable.Empty<MappingEntry>();

            return  type.BaseType!=null && type.BaseType.IsClass
                ? entries.Concat(this.GetMappingEntries(type.BaseType))
                : entries;
        }

        private PropertyChain BuildPropertyChain(Type targetObjectType, string propertyChainByName)
        {
            PropertyChain propertyChain = null;
            PropertyChain propertyCursor = null;
            var properties = propertyChainByName.Split('.');
            foreach (var propertyName in properties)
            {
                var property=propertyCursor==null
                    ?this.GetProperty(targetObjectType, propertyName)
                    :this.GetProperty(propertyCursor.Property.PropertyType, propertyName);

                if (property == null)
                {
                    throw new ArgumentOutOfRangeException(string.Format(
                        "对象{0}不存在名为{1}的属性：{2}", 
                        propertyCursor==null?targetObjectType:propertyCursor.Property.PropertyType, 
                        propertyName, 
                        propertyChainByName));
                }

                var currentPropertyChain=new PropertyChain(property);
                if (propertyCursor != null)
                {
                    propertyCursor.Childen = currentPropertyChain;
                }

                propertyCursor = currentPropertyChain;

                if(propertyChain==null){
                    propertyChain = propertyCursor;
                }
            }

            return propertyChain;
        }

        private PropertyInfo GetProperty(Type type, string propertyName)
        {
            if (!PartAutoMapping.properties.Keys.Contains(type))
            {
                PartAutoMapping.properties.Add(type, type.GetProperties());
            }

            return PartAutoMapping.properties[type].FirstOrDefault(p => p.Name == propertyName);
        }

        private object GetPropertyValue(object instance,PropertyChain propertyChain)
        {
            if (instance == null || propertyChain == null || propertyChain.Property == null || !propertyChain.Property.CanRead)
            {
                return null;
            }

            var result = propertyChain.Property.DynamicPropertyGetValue(instance);
            if (propertyChain.Childen == null)
            {
                return result;
            }

            return this.GetPropertyValue(result, propertyChain.Childen);
        }

        private void SetPropertyValue(object instance, PropertyChain propertyChain, object value)
        {
            if (instance == null || propertyChain == null || propertyChain.Property == null || !propertyChain.Property.CanWrite)
            {
                return;
            }

            if (propertyChain.Childen == null)
            {
                propertyChain.Property.DynamicPropertySetValue(instance, IConvertibleExtensions.As((IConvertible)value, propertyChain.Property.PropertyType));
            }
            else
            {
                var result = propertyChain.Property.DynamicPropertyGetValue(instance);
                this.SetPropertyValue(result, propertyChain.Childen, value);
            }
        }

        //private PropertyInfo GetLastProperty(PropertyChain propertyChain)
        //{
        //    if (propertyChain.Childen == null)
        //    {
        //        return propertyChain.Property;
        //    }

        //    return this.GetLastProperty(propertyChain.Childen);
        //}

        #endregion
    }
}
